//
//  MethodViewController.m
//  ReviewDemo
//
//  Created by 龙 on 2018/3/22.
//  Copyright © 2018年 long. All rights reserved.
//

#import "MethodViewController.h"
#import <objc/runtime.h>
#import "RuntimeMethodHelper.h"

@interface MethodViewController ()

@property (nonatomic ,strong) RuntimeMethodHelper *methodHelper;

- (void)aEmptyMethod1;
- (void)aEmptyMethod2;
- (void)aEmptyMethod3;
- (void)aEmptyMethod4;

@end

@implementation MethodViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    [self excuteMethodGo];
    
    [self aEmptyMethod1];
    [self aEmptyMethod2];
    [self aEmptyMethod3];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)excuteMethodGo
{
    NSLog(@"%s",__func__);
}

#pragma mark - 第一步：动态方法解析

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == NSSelectorFromString(@"aEmptyMethod1"))
    {
        BOOL addSuccess = class_addMethod(self.class, sel, (IMP)addMethod1, "v@:@");
        NSLog(@"第一次拦截 %s 结果：%@",sel_getName(sel),addSuccess == YES ? @"成功" : @"失败");
        return addSuccess;
    }
    
    
    BOOL value = [super resolveInstanceMethod:sel];
    NSLog(@"第一步：方法%s 的入参 %s，最终返回：%d",__func__,sel_getName(sel),value);
    return value;
}

#pragma mark - 第二步：重定向接收者

//如果无法处理消息会继续调用下面的方法，同时在这里Runtime系统实际上是给了一个替换消息接收者的机会，但是替换的对象千万不要是self，那样会进入死循环。
- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == NSSelectorFromString(@"aEmptyMethod2"))
    {
        NSLog(@"第二次拦截 %s 结果成功",sel_getName(aSelector));
        return self.methodHelper;// 将消息转发给_helper来处理
    }
    
    
    id value = [super forwardingTargetForSelector:aSelector];
    NSLog(@"第二步：方法%s 的入参 %s，最终返回：%@",__func__,sel_getName(aSelector),value);
    return value;
}

#pragma mark - 第三步：完整消息转发

//这一步是最后机会将消息转发给其它对象，对象会将未处理的消息相关的selector，target和参数都封装在anInvocation中。forwardInvocation:像未知消息分发中心，将未知消息转发给其它对象。注意的是forwardInvocation:方法只有在消息接收对象无法正常响应消息时才被调用。 Invocation：调用
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    if ([RuntimeMethodHelper instancesRespondToSelector:anInvocation.selector])
    {
        [anInvocation invokeWithTarget:_methodHelper];
    }
}

//必须重写这个方法，消息转发使用这个方法获得的信息创建NSInvocation对象。
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSMethodSignature *sign = [super methodSignatureForSelector:aSelector];
    NSLog(@"第三步：方法%s 的入参 %s，最终返回：%@",__func__,sel_getName(aSelector),sign);
    
    if (sign == nil && aSelector == NSSelectorFromString(@"aEmptyMethod3"))
    {
        if ([RuntimeMethodHelper instancesRespondToSelector:aSelector])
        {
            sign = [RuntimeMethodHelper instanceMethodSignatureForSelector:aSelector];
        }
    }
    
    return sign;
}


#pragma mark - 额外补充的方法

void addMethod1(id self ,SEL _cmd)
{
    NSLog(@"%s",__func__);
}

#pragma mark - setter and getters

- (RuntimeMethodHelper *)methodHelper
{
    if (_methodHelper == nil)
    {
        _methodHelper = [[RuntimeMethodHelper alloc] init];
    }
    return _methodHelper;
}

@end
